package com.apobates.jforum2.troll.replies.biz.impl.dao;

import com.apobates.jforum2.troll.replies.biz.dao.PostsMoodRecordsDao;
import com.apobates.jforum2.troll.replies.entity.MoodCollectResult;
import com.apobates.jforum2.troll.replies.entity.PostsMoodRecords;
import com.apobates.jforum2.troll.utils.persistence.Page;
import com.apobates.jforum2.troll.utils.persistence.Pageable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author xiaofanku
 * @since 20200527
 */
@Repository
public class PostsMoodRecordsDaoImpl implements PostsMoodRecordsDao{
    @PersistenceContext
    private EntityManager entityManager;
    private final static Logger logger = LoggerFactory.getLogger(PostsMoodRecordsDaoImpl.class);
    
    @Override
    public Page<PostsMoodRecords> findAllByPosts(long postsId, Pageable pageable) {
        final long total = findAllByPostsCount(postsId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<PostsMoodRecords> query = entityManager.createQuery("SELECT pm FROM PostsMoodRecords pm WHERE pm.postsId = ?1 ORDER BY pm.entryDateTime DESC", PostsMoodRecords.class).setParameter(1, postsId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<PostsMoodRecords> result = query.getResultStream();
        return new Page<PostsMoodRecords>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<PostsMoodRecords> getResult() {
                return result;
            }
        };
    }
    private long findAllByPostsCount(long postsId) {
        try {
            return entityManager.createQuery("SELECT COUNT(pm) FROM PostsMoodRecords pm WHERE pm.postsId = ?1", Long.class).setParameter(1, postsId).getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllByPostsCount][PostsMoodRecordsDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Stream<PostsMoodRecords> findAllByMood(long postsId, boolean liked) {
        return entityManager.createQuery("SELECT pm FROM PostsMoodRecords pm WHERE pm.postsId = ?1 AND pm.liked = ?2", PostsMoodRecords.class)
                .setParameter(1, postsId)
                .setParameter(2, liked)
                .getResultStream();
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Optional<Boolean> removeMoodRecord(long postsId, long memberId) {
        int affect = entityManager.createQuery("DELETE FROM PostsMoodRecords pm WHERE pm.postsId = ?1 AND pm.memberId = ?2")
                .setParameter(1, postsId)
                .setParameter(2, memberId)
                .executeUpdate();
        return affect == 1 ? Optional.of(true) : Optional.empty();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Optional<Boolean> removeMoodRecord(long postsId, long memberId, boolean liked) {
        int updateVal = (liked)?1:0;
        int affect = entityManager.createQuery("DELETE FROM PostsMoodRecords pm WHERE pm.postsId = ?1 AND pm.memberId = ?2 AND pm.liked = ?3")
                .setParameter(1, postsId)
                .setParameter(2, memberId)
                .setParameter(3, updateVal)
                .executeUpdate();
        return affect == 1 ? Optional.of(true) : Optional.empty();
    }

    @Override
    public Optional<MoodCollectResult> findOneByPosts(long topicId, long postsId) {
        Map<Boolean, Long> data = collectMembers(postsId);
        MoodCollectResult mcr = new MoodCollectResult(
                topicId,
                postsId,
                data.getOrDefault(true, 0L), data.getOrDefault(false, 0L));
        return Optional.of(mcr);
    }

    @Override
    public Map<Boolean, Long> collectMembers(long postsId) {
        Map<Boolean, Long> data = new HashMap<>();
        @SuppressWarnings("unchecked")
        List<Object[]> results = entityManager.createQuery("SELECT pm.liked, COUNT(pm) FROM PostsMoodRecords pm WHERE pm.postsId = ?1 GROUP BY pm.liked").setParameter(1, postsId).getResultList();
        for (Object[] result : results) {
            Boolean name = (Boolean) result[0];
            Long count = 0L;
            
            try {
                count = ((BigDecimal) result[1]).longValue();
            } catch (java.lang.ClassCastException e) {
                count = (Long) result[1];
            }
            data.put(name, count);
        }
        return data;
    }
    
    @Override
    public Map<Long, Map<Boolean, Long>> collectMembers(Collection<Long> postsIdSet) {
        if (postsIdSet == null || postsIdSet.isEmpty()) {
            return Collections.emptyMap();
        }
        Stream<PostsMoodRecords> rs = entityManager.createQuery("SELECT pm FROM PostsMoodRecords pm WHERE pm.postsId IN ?1", PostsMoodRecords.class).setParameter(1, postsIdSet).getResultStream();
        Map<Long, List<PostsMoodRecords>> data = rs.collect(Collectors.groupingBy(PostsMoodRecords::getPostsId));
        //
        Map<Long, Map<Boolean, Long>> result = new HashMap<>();
        for (Entry<Long, List<PostsMoodRecords>> entry : data.entrySet()) {
            Long postsId = entry.getKey();
            Map<Boolean, Long> stats = entry.getValue().stream().collect(Collectors.groupingBy(PostsMoodRecords::isLiked, Collectors.counting()));
            result.put(postsId, stats);
        }
        return result;
    }
    
    @Override
    public Page<PostsMoodRecords> findAll(Pageable pageable) {
        return emptyResult();
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void save(PostsMoodRecords entity) {
        entityManager.persist(entity);
        entityManager.flush();
    }
    
    @Override
    public Optional<PostsMoodRecords> findOne(Long primaryKey) {
        return Optional.empty();
    }
    
    @Override
    public Optional<Boolean> edit(PostsMoodRecords updateEntity) {
        return Optional.empty();
    }
    
    @Override
    public Stream<PostsMoodRecords> findAll() {
        return Stream.empty();
    }
    
    @Override
    public long count() {
        return -1L;
    }
}